package com.shuqing.download.engine.distribute.dispatch.assemble;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.RandomUtil;
import com.shuqing.download.engine.DefaultHandler;
import com.shuqing.download.engine.DownLoadHeader;
import com.shuqing.download.engine.DownLoadTaskDto;
import com.shuqing.download.engine.config.EngineConfig;
import com.shuqing.download.engine.constants.BaseConstants;
import com.shuqing.download.engine.convert.ConvertHelper;
import com.shuqing.download.engine.distribute.dispatch.AbstractActuator;
import com.shuqing.download.engine.distribute.dispatch.TaskContext;
import com.shuqing.download.engine.distribute.dispatch.monitor.MonitorLeader;
import com.shuqing.download.mapper.full.FullQueryResultMapper;
import lombok.SneakyThrows;
import lombok.Synchronized;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.File;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Map;

import static com.shuqing.download.engine.constants.EventEnum.*;

/**
 * 数据组装器执行器
 * @author wangshuqing
 * @date 2023/2/19 12:10
 */
@Slf4j
public abstract class AbstractAssembleActuator extends AbstractActuator {

    @Autowired
    protected EngineConfig engineConfig;
    @Autowired
    private FullQueryResultMapper fullQueryResultMapper;
    @Autowired
    private ConvertHelper convertHelper;


    /**
    * 创建上线文,只是初始化即可
    * @date 2023/3/4 9:47
    * @author wangshuqing
    * @param
     *@return T
    * */
    public abstract AssembleContext createAssembleContext();

    /**
     * 设置执行器上下文的特殊参数
     * @date 2023/2/22 11:04
     * @author wangshuqing
     * @param downLoadTaskDto
     *@param headers
     *@return com.shuqing.download_center_demo.engine.ExportBaseContext
     * */
    public abstract <T extends AssembleContext> void setExecuteExportContext(T context);

    /**
    * 执行结果
    * @date 2023/2/19 12:20
    * @author wangshuqing
    * @param downLoadTaskDto
    * */
    @Override
    public void execute(TaskContext taskContext){
        AssembleContext context=null;
        try{
            DownLoadTaskDto downLoadTaskDto= taskContext.getTask();
            String dataSql=downLoadTaskDto.getSqlStr();
            //构建sql 语句
            String sqlParamsStr=downLoadTaskDto.getSqlParams();
            String querySql=buildQuerySql(dataSql,sqlParamsStr);
            //获取数据字典
            Map<String, Map<String,String>> dictMap=queryDictMap(downLoadTaskDto.getExportHeaders());
            int pageNum=1;
            List<List<Object>> dataLs;
            List<DownLoadHeader> headers=downLoadTaskDto.getExportHeaders();
            //初始化导出上线文
            context=createAssembleContext();
            resetAssembleContext(downLoadTaskDto,context);
            setExecuteExportContext(context);
            monitorLeader.assignSuccessEvent(taskContext, ASSEMBLE_COMPLETE);
            while(true){
                if(context.getTotalSize()>=maxLength()){
                    log.info("下载任务{},已超导出最大限制,结束后续导出操作",downLoadTaskDto.getId());
                    break;
                }
                //从db 获取数据
                dataLs=nextPageData(pageNum,querySql,headers,dictMap);
                if(CollectionUtil.isEmpty(dataLs)){
                    log.info("下载任务{},页数{},总数{},没有数据了,停止后续导出操作",
                            downLoadTaskDto.getId(),pageNum,context.getTotalSize());
                    break;
                }
                monitorLeader.assignSuccessEvent(taskContext, QUERY_DATA_SUCCESS,pageNum);
                ++pageNum;
                //进行数据填充
                executeExport(context,dataLs,pageNum);
                monitorLeader.assignSuccessEvent(taskContext, WRITE_DATA_SUCCESS,dataLs.size(),context.getTotalSize());
            }
        }finally {
            //结束最后 关闭资源
            endExport(context,taskContext);
        }
    }

    private List<DownLoadHeader> resetHeader(List<DownLoadHeader> headers){
       return headers;
    }

    /**
    * 执行导出核心逻辑
    * @date 2023/2/22 11:05
    * @author wangshuqing
    * @param context
    * @param dataLs
    * @param pageNum
    * */
    public abstract void executeExport(AssembleContext context, List<List<Object>> dataLs, int pageNum);

    /**
    * 结束导出任务
    * @date 2023/2/22 11:05
    * @author wangshuqing
    * @param context
    * */
    @SneakyThrows
    public void endExport(AssembleContext context, TaskContext taskContext){
        log.info("下载任务{}完成,关闭所有资源",context.getTaskId());
        //将结果数据赋值给上下文
        if(context!=null){
            //关闭资源
            context.close();
            taskContext.setFileAbsolutePath(context.getFileAbsolutePath());
            taskContext.setFileName(context.getFileName());
            taskContext.setFileNameWithSuffix(context.getFileNameWithSuffix());
            taskContext.setTotalSize(context.getTotalSize());
        }
    }

    /**
    * 获取数据
    * @date 2023/2/22 10:49
    * @author wangshuqing
    * @param pageNum
    * @param sql
    * @param headers
    * @param dictMap
     *@return java.util.List<Map<String,Object>>
    * */
     public List<List<Object>> nextPageData(int pageNum, String sql, List<DownLoadHeader> headers,
                                        Map<String, Map<String,String>> dictMap){
         log.debug("下载任务,获取数据第{}页",pageNum);
         int pageSize=oncePageSize();
         int currIndex=(pageNum-1)*pageSize;
         List<Map<String,Object>> ls=fullQueryResultMapper.queryAnythingByPage(sql,currIndex,pageSize);
         List<List<Object>> exportDatas=null;
         if(CollectionUtil.isNotEmpty(ls)){
             exportDatas=new ArrayList<>(ls.size());
             List<Object> rows=null;
             for(Map<String,Object> map:ls){
                 rows=new ArrayList<>(headers.size());
                 for(DownLoadHeader header:headers){
                     Object exportValue=map.get(header.getSqlFieldCode());
                     //进行格式转换
                     Object value=convertHelper.mapping(engineConfig.getFormat(),exportValue,header.getFormat());
                     rows.add(value);
                 }
                 exportDatas.add(rows);
             }
         }
         return exportDatas;
     }

    /**
    * 构建数据获取的sql
    * @date 2023/2/19 12:32
    * @author wangshuqing
    * @param dataSql
    * @param sqlParamsStr
     *@return java.lang.String
    * */
    public String buildQuerySql(String dataSql,String sqlParamsStr){
        return dataSql;
    }

    /**
    * TODO 获取数据字典
    * @date 2023/2/22 10:48
    * @author wangshuqing
    * @param headers
     *@return java.util.Map<java.lang.String,java.util.Map<java.lang.String,java.lang.String>>
    * */
    public Map<String, Map<String,String>> queryDictMap(List<DownLoadHeader> headers){
        return null;
    }
    /**
    * 每次读取数据的条数
    * @date 2023/2/19 12:33
    * @author wangshuqing
    * @param
     *@return int
    * */
    public int oncePageSize(){
        return engineConfig.getQueryOncePageSize();
    }


    /**
    * 最大 excel 条数
    * @date 2023/2/22 10:53
    * @author wangshuqing
    * @param
     *@return int
    * */
    public int maxLength(){
        return engineConfig.getMaxExportDataSize();
    }


    /**
    * 根据文件名获取文件绝对地址
    * @date 2023/2/24 16:42
    * @author wangshuqing
    * @param suffix
    * @param taskId
     *@return java.lang.String
    * */
    protected String buildFileAbsolutePath(String suffix,Long taskId){
        Calendar calendar=Calendar.getInstance();
        StringBuffer sf=new StringBuffer(engineConfig.getExportFileSavePath());
        sf.append(File.separator)
                .append(calendar.get(Calendar.YEAR))
                .append(calendar.get(Calendar.MONTH))
                .append(File.separator)
                .append(calendar.get(Calendar.DAY_OF_MONTH))
                .append(calendar.get(Calendar.HOUR_OF_DAY))
                .append("_")
                .append(RandomUtil.randomNumbers(3));
        String fileAbsolutePath= sf.toString();
        File file=new File(sf.toString());
        synchronized (AbstractAssembleActuator.class){
            if(!file.exists()){
                file.mkdirs();
            }
        }
        sf.append(File.separator).append(taskId)
        .append(BaseConstants.POINT).append(suffix);
        return sf.toString();
    }

    private void resetAssembleContext(DownLoadTaskDto downLoadTaskDto, AssembleContext assembleContext){
        String fileName=downLoadTaskDto.getFileName();
        String suffix="xlsx";
        String fileNameSuffix=String.format("%s_%s.%s",fileName,downLoadTaskDto.getRetryCount(),suffix);
        String fileAbsolutePath=buildFileAbsolutePath(suffix,downLoadTaskDto.getId());
        assembleContext.setTaskId(downLoadTaskDto.getId());
        assembleContext.setFileAbsolutePath(fileAbsolutePath);
        assembleContext.setFileNameWithSuffix(fileNameSuffix);
        assembleContext.setFileName(fileName);
        assembleContext.setTotalSize(0);
        assembleContext.setExportHeaders(downLoadTaskDto.getExportHeaders());
    }



}
